<?php

namespace app\controller\Util;

use Exception;

class Signer
{
    protected array $ignores = ['sign'];
    private array $params;
    private string $key;

    public function __construct(array $params = [])
    {
        $this->params = $params;
    }

    public function setParams(array $params): Signer
    {
        $this->params = $params;

        return $this;
    }

    public function setKey(string $key): Signer
    {
        $this->key = $key;

        return $this;
    }

    /**
     * @throws Exception
     */
    public function sign(): string
    {
        return $this->signWithRSA();
    }

    public function getContentToSign(): string
    {
        $params = $this->getParamsToSign();

        return urldecode(http_build_query($params));
    }

    public function getParamsToSign(): array
    {
        $params = $this->params;

        $this->unsetKeys($params);

        $params = $this->filter($params);

        $this->sort($params);

        return $params;
    }

    protected function unsetKeys(&$params)
    {
        foreach ($this->getIgnores() as $key) {
            unset($params[$key]);
        }
    }

    public function getIgnores(): array
    {
        return $this->ignores;
    }

    public function setIgnores(array $ignores): Signer
    {
        $this->ignores = $ignores;

        return $this;
    }

    private function filter($params): array
    {
        return array_filter($params, function($value) {
            return is_string($value) && strlen($value) > 0;
        });
    }


    protected function sort(&$params)
    {
        ksort($params);
    }

    /**
     * @throws Exception
     */
    public function signWithRSA(): string
    {
        $content = $this->getContentToSign();

        $res = openssl_pkey_get_private($this->key);

        $sign = null;
        try {
            openssl_sign($content, $sign, $res, OPENSSL_ALGO_SHA256);
        } catch (Exception $e) {
            if ($e->getCode() == 2) {
                throw new Exception("应用私钥格式有误", $e->getCode());
            }
        }

// 删除 openssl_pkey_free($res) 这一行
// 不再显式释放 $res，PHP 会自动管理
        return base64_encode((string)$sign);


    }

    /**
     * @throws Exception
     */
    public function verify(string $sign): bool
    {
        return $this->verifyWithRSA($sign);
    }

    /**
     * @throws Exception
     */
    public function verifyWithRSA(string $sign): bool
    {
        $content = $this->getContentToSign();

        $res = openssl_pkey_get_public($this->key);

        if (!$res) {
            throw new Exception("公钥格式有误");
        }

        $result = (bool) openssl_verify($content, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);

        openssl_free_key($res);

        return $result;
    }
}
